base: preserve color in premutiplied alpha
authorØyvind Kolås <pippin@gimp.org>
Fri, 24 Aug 2018 17:16:34 +0000 (19:16 +0200)
committerØyvind Kolås <pippin@gimp.org>
Fri, 24 Aug 2018 23:18:25 +0000 (01:18 +0200)
Alpha values below BABL_ALPHA_FLOOR gets stored in the components
as BABL_ALPHA_FLOOR with alpha also set to BABL_ALPHA_FLOOR,
recovery from pre-multiplied to non-premultiplied is done as normal.
BABL_FLOOR is set to be 1/65536.0 with a hope that this gives us
approximately 8bit of color data preserved when using half-float.

Upon encountering exactly BABL_ALPHA_FLOOR in premultiplied conversion
to non-premultiplied interprets this as 0.0, making pure 0.0 values
round trip correctly from RGBA to RaGaBaA back to RGBA.

This makes a lot of extensions invalid - as expected but it already
allows testing the behavior of GEGL and GIMP with this additional
feature from babl.

babl/babl-internal.h
babl/base/babl-base.h
babl/base/model-gray.c
babl/base/model-rgb.c
babl/base/util.h

index 0f8a07c69d065db2058714c2cf9a312ca04d9c0f..8f3143409ff8c6c431c15660058b7d6f32848806 100644 (file)
@@ -30,6 +30,7 @@
 #define BABL_MAX_COMPONENTS       32
 #define BABL_CONVERSIONS          5
 
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
index 80a5b541481e9c2c8e2f0541fc4191a9a227143a..bc67f5c30b398a1c3f8c66b02b26c04f0791b47d 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef _BABL_BASE_H
 #define _BABL_BASE_H
 
+
 void babl_base_init (void);
 void babl_base_destroy (void);
 void babl_formats_init (void);
index 9d3677b6adec1dc9082bba2ca80c50fc5a3bb505..eea9ebf0ac3a511d1bfa6f1ba38218139d8fdf90 100644 (file)
@@ -425,16 +425,16 @@ gray_alpha_premultiplied_to_rgba (Babl   *conversion,
   while (n--)
     {
       double luminance = *(double *) src[0];
-      double alpha     = *(double *) src[1];
-
-      if (alpha > BABL_ALPHA_THRESHOLD)
-        {
-          luminance = luminance / alpha;
-        }
+      double alpha;
+      alpha = *(double *) src[1];
+      if (alpha == 0)
+        luminance = 0;
       else
-        {
-          luminance = 0.0;
-        }
+      {
+        luminance = luminance / alpha;
+        if (alpha == BABL_ALPHA_FLOOR)
+          alpha = 0.0;
+      }
 
       *(double *) dst[0] = luminance;
       *(double *) dst[1] = luminance;
@@ -469,17 +469,20 @@ rgba_to_gray_alpha_premultiplied (Babl   *conversion,
       double red   = *(double *) src[0];
       double green = *(double *) src[1];
       double blue  = *(double *) src[2];
-      double alpha = *(double *) src[3];
       double luminance;
+      double alpha, alpha_used;
+      alpha_used = alpha = *(double *) src[3];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
 
       luminance = red * RGB_LUMINANCE_RED +
                   green * RGB_LUMINANCE_GREEN +
                   blue * RGB_LUMINANCE_BLUE;
 
-      luminance *= alpha;
+      luminance *= alpha_used;
 
       *(double *) dst[0] = luminance;
-      *(double *) dst[1] = alpha;
+      *(double *) dst[1] = alpha_used;
       BABL_PLANAR_STEP
     }
 }
@@ -498,15 +501,18 @@ non_premultiplied_to_premultiplied (Babl  *conversion,
 
   while (n--)
     {
-      double alpha;
       int    band;
+      double alpha, alpha_used;
+
+      alpha_used = alpha = *(double *) src[src_bands-1];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
 
-      alpha = *(double *) src[src_bands - 1];
       for (band = 0; band < src_bands - 1; band++)
         {
-          *(double *) dst[band] = *(double *) src[band] * alpha;
+          *(double *) dst[band] = *(double *) src[band] * alpha_used;
         }
-      *(double *) dst[dst_bands - 1] = alpha;
+      *(double *) dst[dst_bands - 1] = alpha_used;
 
       BABL_PLANAR_STEP
     }
@@ -526,21 +532,19 @@ premultiplied_to_non_premultiplied (Babl  *conversion,
 
   while (n--)
     {
-      double alpha;
       int    band;
+      double alpha;
+      alpha = *(double *) src[src_bands-1];
 
-      alpha = *(double *) src[src_bands - 1];
       for (band = 0; band < src_bands - 1; band++)
         {
-          if (alpha > BABL_ALPHA_THRESHOLD)
-            {
-              *(double *) dst[band] = *(double *) src[band] / alpha;
-            }
+          if (alpha == 0.0)
+            *(double *) dst[band] = 0;
           else
-            {
-              *(double *) dst[band] = 0.0;
-            }
+            *(double *) dst[band] = *(double *) src[band] / alpha;
         }
+      if (alpha == BABL_ALPHA_FLOOR)
+        alpha = 0.0;
       *(double *) dst[dst_bands - 1] = alpha;
 
       BABL_PLANAR_STEP
@@ -564,18 +568,20 @@ rgba2gray_nonlinear_premultiplied (Babl *conversion,
       double red   = ((double *) src)[0];
       double green = ((double *) src)[1];
       double blue  = ((double *) src)[2];
-      double alpha = ((double *) src)[3];
-
       double luminance;
       double luma;
+      double alpha, alpha_used;
+      alpha_used = alpha = ((double *) src)[3];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
 
       luminance = red * RGB_LUMINANCE_RED +
                   green * RGB_LUMINANCE_GREEN +
                   blue * RGB_LUMINANCE_BLUE;
       luma = babl_trc_from_linear (trc, luminance);
 
-      ((double *) dst)[0] = luma * alpha;
-      ((double *) dst)[1] = alpha;
+      ((double *) dst)[0] = luma * alpha_used;
+      ((double *) dst)[1] = alpha_used;
 
       src += 4 * sizeof (double);
       dst += 2 * sizeof (double);
@@ -595,16 +601,19 @@ gray_nonlinear_premultiplied2rgba (Babl *conversion,
   while (n--)
     {
       double luma  = ((double *) src)[0];
-      double alpha = ((double *) src)[1];
       double luminance;
-
-      if (alpha > BABL_ALPHA_THRESHOLD)
-        luma      = luma / alpha;
+      double alpha;
+      alpha = ((double *) src)[1];
+      if (alpha == 0.0)
+        luma = 0.0;
       else
-        luma      = 0.0;
+        luma = luma / alpha;
 
       luminance = babl_trc_to_linear (trc, luma);
 
+      if (alpha == BABL_ALPHA_FLOOR)
+        alpha = 0.0;
+
       ((double *) dst)[0] = luminance;
       ((double *) dst)[1] = luminance;
       ((double *) dst)[2] = luminance;
index 66e16abc03d12f80924bf2492ed8fe9cb7d48be2..c2c46f2068477fab1f5a86ff57ea7e0b87d88109 100644 (file)
@@ -306,6 +306,7 @@ g3_nonlinear_to_linear (Babl  *conversion,
     }
 }
 
+
 static void
 non_premultiplied_to_premultiplied (Babl  *conversion,
                                     int    src_bands,
@@ -322,14 +323,18 @@ non_premultiplied_to_premultiplied (Babl  *conversion,
   while (n--)
     {
       double alpha;
+      double alpha_used;
       int    band;
 
-      alpha = *(double *) src[src_bands - 1];
+      alpha_used = alpha = *(double *) src[src_bands - 1];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
+
       for (band = 0; band < src_bands - 1; band++)
         {
-          *(double *) dst[band] = *(double *) src[band] * alpha;
+          *(double *) dst[band] = *(double *) src[band] * alpha_used;
         }
-      *(double *) dst[dst_bands - 1] = alpha;
+      *(double *) dst[dst_bands - 1] = alpha_used;
 
       BABL_PLANAR_STEP
     }
@@ -351,20 +356,21 @@ premultiplied_to_non_premultiplied (Babl  *conversion,
   while (n--)
     {
       double alpha;
+      double recip_alpha;
       int    band;
 
       alpha = *(double *) src[src_bands - 1];
-      if (alpha > BABL_ALPHA_THRESHOLD)
-        {
-          double recip_alpha = 1.0 / alpha;
-          for (band = 0; band < src_bands - 1; band++)
-            *(double *) dst[band] = *(double *) src[band] * recip_alpha;
-        }
+      if (alpha == 0.0)
+         recip_alpha = 0.0;
       else
-        {
-          for (band = 0; band < src_bands - 1; band++)
-            *(double *) dst[band] = 0.0;
-        }
+      {
+        recip_alpha  = 1.0 / alpha;
+        if (alpha == BABL_ALPHA_FLOOR)
+          alpha = 0.0; // making 0 round-trip to zero, causing discontinuity
+      }
+
+      for (band = 0; band < src_bands - 1; band++)
+        *(double *) dst[band] = *(double *) src[band] * recip_alpha;
       *(double *) dst[dst_bands - 1] = alpha;
 
       BABL_PLANAR_STEP
@@ -384,11 +390,14 @@ rgba2rgba_nonlinear_premultiplied (Babl *conversion,
 
   while (n--)
     {
-      double alpha = ((double *) src)[3];
-      ((double *) dst)[0] = babl_trc_from_linear (trc[0], ((double *) src)[0]) * alpha;
-      ((double *) dst)[1] = babl_trc_from_linear (trc[1], ((double *) src)[1]) * alpha;
-      ((double *) dst)[2] = babl_trc_from_linear (trc[2], ((double *) src)[2]) * alpha;
-      ((double *) dst)[3] = alpha;
+      double alpha, alpha_used;
+      alpha_used = alpha = ((double *) src)[3];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
+      ((double *) dst)[0] = babl_trc_from_linear (trc[0], ((double *) src)[0]) * alpha_used;
+      ((double *) dst)[1] = babl_trc_from_linear (trc[1], ((double *) src)[1]) * alpha_used;
+      ((double *) dst)[2] = babl_trc_from_linear (trc[2], ((double *) src)[2]) * alpha_used;
+      ((double *) dst)[3] = alpha_used;
       src                += 4 * sizeof (double);
       dst                += 4 * sizeof (double);
     }
@@ -407,19 +416,22 @@ rgba_nonlinear_premultiplied2rgba (Babl *conversion,
 
   while (n--)
     {
-      double alpha = ((double *) src)[3];
-      if (alpha > BABL_ALPHA_THRESHOLD)
-        {
+      double alpha;
+      alpha = ((double *) src)[3];
+      if (alpha == 0)
+      {
+          ((double *) dst)[0] = 0;
+          ((double *) dst)[1] = 0;
+          ((double *) dst)[2] = 0;
+      }
+      else
+      {
           ((double *) dst)[0] = babl_trc_to_linear (trc[0], ((double *) src)[0] / alpha);
           ((double *) dst)[1] = babl_trc_to_linear (trc[1], ((double *) src)[1] / alpha);
           ((double *) dst)[2] = babl_trc_to_linear (trc[2], ((double *) src)[2] / alpha);
-        }
-      else
-        {
-          ((double *) dst)[0] = 0.0;
-          ((double *) dst)[1] = 0.0;
-          ((double *) dst)[2] = 0.0;
-        }
+          if (alpha == BABL_ALPHA_FLOOR)
+            alpha = 0;
+      }
       ((double *) dst)[3] = alpha;
 
       src += 4 * sizeof (double);
@@ -547,11 +559,14 @@ rgba2rgba_perceptual_premultiplied (Babl *conversion,
 
   while (n--)
     {
-      double alpha = ((double *) src)[3];
-      ((double *) dst)[0] = babl_trc_from_linear (trc, ((double *) src)[0]) * alpha;
-      ((double *) dst)[1] = babl_trc_from_linear (trc, ((double *) src)[1]) * alpha;
-      ((double *) dst)[2] = babl_trc_from_linear (trc, ((double *) src)[2]) * alpha;
-      ((double *) dst)[3] = alpha;
+      double alpha, alpha_used;
+      alpha_used = alpha = ((double *) src)[3];
+      if (alpha < BABL_ALPHA_FLOOR)
+         alpha_used = BABL_ALPHA_FLOOR;
+      ((double *) dst)[0] = babl_trc_from_linear (trc, ((double *) src)[0]) * alpha_used;
+      ((double *) dst)[1] = babl_trc_from_linear (trc, ((double *) src)[1]) * alpha_used;
+      ((double *) dst)[2] = babl_trc_from_linear (trc, ((double *) src)[2]) * alpha_used;
+      ((double *) dst)[3] = alpha_used;
       src                += 4 * sizeof (double);
       dst                += 4 * sizeof (double);
     }
@@ -560,28 +575,31 @@ rgba2rgba_perceptual_premultiplied (Babl *conversion,
 
 static void
 rgba_perceptual_premultiplied2rgba (Babl *conversion,
-                                   char           *src,
-                                   char           *dst,
-                                   long            samples)
+                                    char *src,
+                                    char *dst,
+                                    long  samples)
 {
   const Babl *trc  = perceptual_trc;
   long n = samples;
 
   while (n--)
     {
-      double alpha = ((double *) src)[3];
-      if (alpha > BABL_ALPHA_THRESHOLD)
-        {
-          ((double *) dst)[0] = babl_trc_to_linear (trc, ((double *) src)[0] / alpha);
-          ((double *) dst)[1] = babl_trc_to_linear (trc, ((double *) src)[1] / alpha);
-          ((double *) dst)[2] = babl_trc_to_linear (trc, ((double *) src)[2] / alpha);
-        }
+      double alpha;
+      alpha = ((double *) src)[3];
+      if (alpha == 0)
+      {
+        ((double *) dst)[0] = 0;
+        ((double *) dst)[1] = 0;
+        ((double *) dst)[2] = 0;
+      }
       else
-        {
-          ((double *) dst)[0] = 0.0;
-          ((double *) dst)[1] = 0.0;
-          ((double *) dst)[2] = 0.0;
-        }
+      {
+         ((double *) dst)[0] = babl_trc_to_linear (trc, ((double *) src)[0] / alpha);
+         ((double *) dst)[1] = babl_trc_to_linear (trc, ((double *) src)[1] / alpha);
+         ((double *) dst)[2] = babl_trc_to_linear (trc, ((double *) src)[2] / alpha);
+         if (alpha == BABL_ALPHA_FLOOR)
+           alpha = 0.0;
+      }
       ((double *) dst)[3] = alpha;
 
       src += 4 * sizeof (double);
index b30c9352a6106d4502d871619ea95d10c2b6abea..4d9f536aee19241d8dd6e6fd5ceaa636b319d13b 100644 (file)
 #include "pow-24.h"
 
 /* Alpha threshold used in the reference implementation for
- * un-pre-multiplication of color data:
+ * un-pre-multiplication of color data, deprecated in favor of the following
  *
  * 0.01 / (2^16 - 1)
  */
 #define BABL_ALPHA_THRESHOLD 0.000000152590219
 
+ /* values below this are stored premultiplied with this value  */
+#define BABL_ALPHA_FLOOR (1/65536.0)
+
 #define BABL_PLANAR_SANITY  \
   {                         \
     assert(src_bands>0);    \